Skip to content

Fix bugs in packed ORAM with entries spanning several words - #1802

Merged
mkskeller merged 1 commit into
data61:masterfrom
rdeviti:packed-oram-used-bits
Nov 9, 2026
Merged

mkskeller merged 1 commit into
data61:masterfrom
rdeviti:packed-oram-used-bits

Conversation

@rdeviti

@rdeviti rdeviti commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

I found this when I wanted to store an array in each entry of a packed ORAM, which makes each entry span several words.

For such entries, entries_per_element is a fraction, so used_bits = entries_per_element * sum(entry_size) is a float, for example 30.0 for entry_size=(40, 40, 40) on a 64-bit ring.

With Scripts/compile-run.py -E ring, -E sy-rep-ring or -E rep-field, the following snippet raises TypeError: 'float' object cannot be interpreted as an integer in random_block:

from Compiler.oram import OptimalPackedORAM
o = OptimalPackedORAM(1000, entry_size=(40, 40, 40), value_type=sint, init_rounds=4, random_init=True)
o[sint(0)] = (sint(1), sint(2), sint(3))
print_ln('%s', o[sint(0)][2].reveal())

With -E rep-field, the following snippet raises Invalid argument 0 "81.0" to instruction: reqbl 81.0:

from Compiler.oram import OptimalPackedORAM
o = OptimalPackedORAM(1000, entry_size=(40,) * 4, value_type=sint, init_rounds=4)
o[sint(0)] = (sint(1), sint(2), sint(3), sint(4))
print_ln('%s', o[sint(0)][3].reveal())

If used_bits is lower than the number of bits in a word, the compiler accepts a prime given with --prime that is too short for the words; the words then overflow, and reads return wrong values. For example, each word of the following snippet holds two 40-bit values, but used_bits is 60.0. Compiled with --prime and run with -P, both set to the 80-bit prime 2**79 + 23, the snippet prints 1099511627752 instead of 1099511627775:

from Compiler.oram import OptimalPackedORAM
o = OptimalPackedORAM(1000, entry_size=(40,) * 6, value_type=sint, init_rounds=4)
o[sint(0)] = tuple(sint(2**40 - 1) for i in range(6))
print_ln('%s', o[sint(0)][5].reveal())

With sbits values, the storage creates register types of used_bits bits, so packed circuit ORAM, for example circuit_oram.RecursiveCircuitIndexStructure, fails for these entries with TypeError: unsupported operand type(s) for <<: 'int' and 'float'.

Fix

For entries spanning several words, the storage now receives the number of bits in each word, sum(x) for each x in split_sizes, instead of used_bits, and random_init draws exactly that many random bits for each word.

Testing

Starting from these snippets, I varied the ORAM (linear, tree and path ORAM, with and without empty entries), the entry size (from one word to 96 words, with words of the same or of different sizes), the initialization (default, with init_rounds and with random_init) and the protocol (ring, sy-rep-ring and rep-field), and read back the entries I wrote. With the fix, all of them compile and read back correctly, including the first two snippets with all three protocols, and so does RecursiveCircuitIndexStructure with sbits. For the third snippet, the compiler now refuses primes of 80 and 81 bits with required bit length 80 too much, as it does for any 80-bit value, instead of computing a wrong result. With an 82-bit prime, it reads back correctly.

Without --prime, programs that compiled before, including oram_tutorial, dijkstra_example, gale-shapley_tutorial and test_path_oblivious_heap, compile to the same bytecode. The only exception is on prime fields, where the minimum prime length that a program declares with reqbl can now be larger, because it is computed from the longest word rather than from used_bits.

@mkskeller
mkskeller merged commit d1e3f55 into data61:master Nov 9, 2026
3 checks passed
@mkskeller

Copy link
Copy Markdown
Member

Thank you

@rdeviti
rdeviti deleted the packed-oram-used-bits branch November 9, 2026 17:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants